home *** CD-ROM | disk | FTP | other *** search
/ Micromanía 92 / CDMM92_1.ISO / SOF 2 SDK / sof2sdk-101.msi / _92D6AC311BB48EBA344BBABC89DA6AB0 / _C9A444D6A6F74852B5FABD7EC5FDF28F < prev    next >
Encoding:
Text File  |  2002-06-13  |  48.4 KB  |  2,340 lines

  1. // Copyright (C) 2001-2002 Raven Software.
  2. //
  3. // ai_wpnav.c
  4.  
  5. #include "g_local.h"
  6. #include "q_shared.h"
  7. #include "botlib.h"
  8. #include "ai_main.h"
  9.  
  10. float gWPRenderTime = 0;
  11. float gDeactivated = 0;
  12. float gBotEdit = 0;
  13. int gWPRenderedFrame = 0;
  14. wpobject_t *gWPArray[MAX_WPARRAY_SIZE];
  15. int gWPNum = 0;
  16. int gLastPrintedIndex = -1;
  17.  
  18. nodeobject_t nodetable[MAX_NODETABLE_SIZE];
  19. int nodenum; //so we can connect broken trails
  20.  
  21. #define    MAX_FLAGSTR_SIZE        128
  22.  
  23. char *GetFlagStr( int flags )
  24. {
  25.     char *flagstr;
  26.     int i;
  27.  
  28.     flagstr = (char *)B_TempAlloc(MAX_FLAGSTR_SIZE);
  29.     i = 0;
  30.  
  31.     if (!flags)
  32.     {
  33.         strcpy(flagstr, "none\0");
  34.         goto fend;
  35.     }
  36.  
  37.     if (flags & WPFLAG_JUMP)
  38.     {
  39.         flagstr[i] = 'j';
  40.         i++;
  41.     }
  42.  
  43.     if (flags & WPFLAG_DUCK)
  44.     {
  45.         flagstr[i] = 'd';
  46.         i++;
  47.     }
  48.  
  49.     if (flags & WPFLAG_SNIPEORCAMPSTAND)
  50.     {
  51.         flagstr[i] = 'c';
  52.         i++;
  53.     }
  54.  
  55.     if (flags & WPFLAG_WAITFORFUNC)
  56.     {
  57.         flagstr[i] = 'f';
  58.         i++;
  59.     }
  60.  
  61.     if (flags & WPFLAG_SNIPEORCAMP)
  62.     {
  63.         flagstr[i] = 's';
  64.         i++;
  65.     }
  66.  
  67.     if (flags & WPFLAG_ONEWAY_FWD)
  68.     {
  69.         flagstr[i] = 'x';
  70.         i++;
  71.     }
  72.  
  73.     if (flags & WPFLAG_ONEWAY_BACK)
  74.     {
  75.         flagstr[i] = 'y';
  76.         i++;
  77.     }
  78.  
  79.     if (flags & WPFLAG_GOALPOINT)
  80.     {
  81.         flagstr[i] = 'g';
  82.         i++;
  83.     }
  84.  
  85.     if (flags & WPFLAG_NOVIS)
  86.     {
  87.         flagstr[i] = 'n';
  88.         i++;
  89.     }
  90.  
  91.     if (flags & WPFLAG_NOMOVEFUNC)
  92.     {
  93.         flagstr[i] = 'm';
  94.         i++;
  95.     }
  96.  
  97.     if (flags & WPFLAG_RED_FLAG)
  98.     {
  99.         if (i)
  100.         {
  101.             flagstr[i] = ' ';
  102.             i++;
  103.         }
  104.         flagstr[i] = 'r';
  105.         i++;
  106.         flagstr[i] = 'e';
  107.         i++;
  108.         flagstr[i] = 'd';
  109.         i++;
  110.         flagstr[i] = ' ';
  111.         i++;
  112.         flagstr[i] = 'f';
  113.         i++;
  114.         flagstr[i] = 'l';
  115.         i++;
  116.         flagstr[i] = 'a';
  117.         i++;
  118.         flagstr[i] = 'g';
  119.         i++;
  120.     }
  121.  
  122.     if (flags & WPFLAG_BLUE_FLAG)
  123.     {
  124.         if (i)
  125.         {
  126.             flagstr[i] = ' ';
  127.             i++;
  128.         }
  129.         flagstr[i] = 'b';
  130.         i++;
  131.         flagstr[i] = 'l';
  132.         i++;
  133.         flagstr[i] = 'u';
  134.         i++;
  135.         flagstr[i] = 'e';
  136.         i++;
  137.         flagstr[i] = ' ';
  138.         i++;
  139.         flagstr[i] = 'f';
  140.         i++;
  141.         flagstr[i] = 'l';
  142.         i++;
  143.         flagstr[i] = 'a';
  144.         i++;
  145.         flagstr[i] = 'g';
  146.         i++;
  147.     }
  148.  
  149.     flagstr[i] = '\0';
  150.  
  151.     if (i == 0)
  152.     {
  153.         strcpy(flagstr, "unknown\0");
  154.     }
  155.  
  156. fend:
  157.     return flagstr;
  158. }
  159.  
  160. void G_TestLine(vec3_t start, vec3_t end, int color, int time)
  161. {
  162.     gentity_t *te;
  163.  
  164.     te = G_TempEntity( start, EV_TESTLINE );
  165.     VectorCopy(start, te->s.origin);
  166.     VectorCopy(end, te->s.origin2);
  167.     te->s.weapon = color;
  168.     te->r.svFlags |= SVF_BROADCAST;
  169. }
  170.  
  171. void BotWaypointRender(void)
  172. {
  173.     int i, n;
  174.     int inc_checker;
  175.     int bestindex;
  176.     int gotbestindex;
  177.     float bestdist;
  178.     float checkdist;
  179.     gentity_t *plum;
  180.     gentity_t *viewent;
  181.     char *flagstr;
  182.     vec3_t a;
  183.  
  184.     if (!gBotEdit)
  185.     {
  186.         return;
  187.     }
  188.  
  189.     bestindex = 0;
  190.  
  191.     if (gWPRenderTime > level.time)
  192.     {
  193.         goto checkprint;
  194.     }
  195.  
  196.     gWPRenderTime = level.time + 100;
  197.  
  198.     i = gWPRenderedFrame;
  199.     inc_checker = gWPRenderedFrame;
  200.  
  201.     while (i < gWPNum)
  202.     {
  203.         if (gWPArray[i] && gWPArray[i]->inuse)
  204.         {
  205.             if (gWPArray[i+1] && gWPArray[i+1]->inuse)
  206.             {
  207.                 plum = G_TempEntity( gWPArray[i]->origin, EV_BOTWAYPOINT );
  208.                 VectorCopy(gWPArray[i+1]->origin, plum->s.angles);
  209.                 plum->r.svFlags |= SVF_BROADCAST;
  210.                 plum->s.time = i;
  211.             }
  212.  
  213.             n = 0;
  214.  
  215.             while (n < gWPArray[i]->neighbornum)
  216.             {
  217.                 if (gWPArray[i]->neighbors[n].forceJumpTo && gWPArray[gWPArray[i]->neighbors[n].num])
  218.                 {
  219.                     G_TestLine(gWPArray[i]->origin, gWPArray[gWPArray[i]->neighbors[n].num]->origin, 0x0000ff, 5000);
  220.                 }
  221.                 n++;
  222.             }
  223.  
  224.             gWPRenderedFrame++;
  225.         }
  226.         else
  227.         {
  228.             gWPRenderedFrame = 0;
  229.             break;
  230.         }
  231.  
  232.         if ((i - inc_checker) > 4)
  233.         {
  234.             break; //don't render too many at once
  235.         }
  236.         i++;
  237.     }
  238.  
  239.     if (i >= gWPNum)
  240.     {
  241.         gWPRenderTime = level.time + 1500; //wait a bit after we finish doing the whole trail
  242.         gWPRenderedFrame = 0;
  243.     }
  244.  
  245. checkprint:
  246.  
  247.     if (!bot_wp_info.value)
  248.     {
  249.         return;
  250.     }
  251.  
  252.     viewent = &g_entities[0]; //only show info to the first client
  253.  
  254.     if (!viewent || !viewent->client)
  255.     { //client isn't in the game yet?
  256.         return;
  257.     }
  258.  
  259.     bestdist = 256; //max distance for showing point info
  260.     gotbestindex = 0;
  261.  
  262.     i = 0;
  263.  
  264.     while (i < gWPNum)
  265.     {
  266.         if (gWPArray[i])
  267.         {
  268.             VectorSubtract(viewent->client->ps.origin, gWPArray[i]->origin, a);
  269.  
  270.             checkdist = VectorLength(a);
  271.  
  272.             if (checkdist < bestdist)
  273.             {
  274.                 bestdist = checkdist;
  275.                 bestindex = i;
  276.                 gotbestindex = 1;
  277.             }
  278.         }
  279.         i++;
  280.     }
  281.  
  282.     if (gotbestindex && bestindex != gLastPrintedIndex)
  283.     {
  284.         flagstr = GetFlagStr(gWPArray[bestindex]->flags);
  285.         gLastPrintedIndex = bestindex;
  286.         Com_Printf(S_COLOR_YELLOW "Waypoint %i\nFlags - %i (%s)\nOrigin - (%i %i %i)\n", (int)(gWPArray[bestindex]->index), (int)(gWPArray[bestindex]->flags), flagstr, (int)(gWPArray[bestindex]->origin[0]), (int)(gWPArray[bestindex]->origin[1]), (int)(gWPArray[bestindex]->origin[2]));
  287.         trap_VM_LocalTempFree(MAX_FLAGSTR_SIZE);
  288.  
  289.         /*
  290.         plum = G_TempEntity( gWPArray[bestindex]->origin, EV_BOTWAYPOINT );
  291.         plum->r.svFlags |= SVF_BROADCAST;
  292.         plum->s.time = bestindex; //render it once
  293.         //Don't bother at the moment. Number display is broken.
  294.         */
  295.     }
  296.     else if (!gotbestindex)
  297.     {
  298.         gLastPrintedIndex = -1;
  299.     }
  300. }
  301.  
  302. void TransferWPData(int from, int to)
  303. {
  304.     if (!gWPArray[to])
  305.     {
  306.         gWPArray[to] = (wpobject_t *)B_Alloc(sizeof(wpobject_t));
  307.     }
  308.  
  309.     if (!gWPArray[to])
  310.     {
  311.         Com_Printf(S_COLOR_RED "FATAL ERROR: Could not allocated memory for waypoint\n");
  312.     }
  313.  
  314.     gWPArray[to]->flags = gWPArray[from]->flags;
  315.     gWPArray[to]->weight = gWPArray[from]->weight;
  316.     gWPArray[to]->associated_entity = gWPArray[from]->associated_entity;
  317.     gWPArray[to]->disttonext = gWPArray[from]->disttonext;
  318.     gWPArray[to]->forceJumpTo = gWPArray[from]->forceJumpTo;
  319.     gWPArray[to]->index = to;
  320.     gWPArray[to]->inuse = gWPArray[from]->inuse;
  321.     VectorCopy(gWPArray[from]->origin, gWPArray[to]->origin);
  322. }
  323.  
  324. void CreateNewWP(vec3_t origin, int flags)
  325. {
  326.     if (gWPNum >= MAX_WPARRAY_SIZE)
  327.     {
  328.         Com_Printf(S_COLOR_YELLOW "Warning: Waypoint limit hit (%i)\n", MAX_WPARRAY_SIZE);
  329.         return;
  330.     }
  331.  
  332.     if (!gWPArray[gWPNum])
  333.     {
  334.         gWPArray[gWPNum] = (wpobject_t *)B_Alloc(sizeof(wpobject_t));
  335.     }
  336.  
  337.     if (!gWPArray[gWPNum])
  338.     {
  339.         Com_Printf(S_COLOR_RED "ERROR: Could not allocated memory for waypoint\n");
  340.     }
  341.  
  342.     gWPArray[gWPNum]->flags = flags;
  343.     gWPArray[gWPNum]->weight = 0; //calculated elsewhere
  344.     gWPArray[gWPNum]->associated_entity = ENTITYNUM_NONE; //set elsewhere
  345.     gWPArray[gWPNum]->forceJumpTo = 0;
  346.     gWPArray[gWPNum]->disttonext = 0; //calculated elsewhere
  347.     gWPArray[gWPNum]->index = gWPNum;
  348.     gWPArray[gWPNum]->inuse = 1;
  349.     VectorCopy(origin, gWPArray[gWPNum]->origin);
  350.     gWPNum++;
  351. }
  352.  
  353. void CreateNewWP_FromObject(wpobject_t *wp)
  354. {
  355.     int i;
  356.  
  357.     if (gWPNum >= MAX_WPARRAY_SIZE)
  358.     {
  359.         return;
  360.     }
  361.  
  362.     if (!gWPArray[gWPNum])
  363.     {
  364.         gWPArray[gWPNum] = (wpobject_t *)B_Alloc(sizeof(wpobject_t));
  365.     }
  366.  
  367.     if (!gWPArray[gWPNum])
  368.     {
  369.         Com_Printf(S_COLOR_RED "ERROR: Could not allocated memory for waypoint\n");
  370.     }
  371.  
  372.     gWPArray[gWPNum]->flags = wp->flags;
  373.     gWPArray[gWPNum]->weight = wp->weight;
  374.     gWPArray[gWPNum]->associated_entity = wp->associated_entity;
  375.     gWPArray[gWPNum]->disttonext = wp->disttonext;
  376.     gWPArray[gWPNum]->forceJumpTo = wp->forceJumpTo;
  377.     gWPArray[gWPNum]->index = gWPNum;
  378.     gWPArray[gWPNum]->inuse = 1;
  379.     VectorCopy(wp->origin, gWPArray[gWPNum]->origin);
  380.     gWPArray[gWPNum]->neighbornum = wp->neighbornum;
  381.  
  382.     i = wp->neighbornum;
  383.  
  384.     while (i >= 0)
  385.     {
  386.         gWPArray[gWPNum]->neighbors[i].num = wp->neighbors[i].num;
  387.         gWPArray[gWPNum]->neighbors[i].forceJumpTo = wp->neighbors[i].forceJumpTo;
  388.  
  389.         i--;
  390.     }
  391.  
  392.     if (gWPArray[gWPNum]->flags & WPFLAG_RED_FLAG)
  393.     {
  394.         flagRed = gWPArray[gWPNum];
  395.         oFlagRed = flagRed;
  396.     }
  397.     else if (gWPArray[gWPNum]->flags & WPFLAG_BLUE_FLAG)
  398.     {
  399.         flagBlue = gWPArray[gWPNum];
  400.         oFlagBlue = flagBlue;
  401.     }
  402.  
  403.     gWPNum++;
  404. }
  405.  
  406. void RemoveWP(void)
  407. {
  408.     if (gWPNum <= 0)
  409.     {
  410.         return;
  411.     }
  412.  
  413.     gWPNum--;
  414.  
  415.     if (!gWPArray[gWPNum] || !gWPArray[gWPNum]->inuse)
  416.     {
  417.         return;
  418.     }
  419.  
  420.     //B_Free((wpobject_t *)gWPArray[gWPNum]);
  421.     if (gWPArray[gWPNum])
  422.     {
  423.         memset( gWPArray[gWPNum], 0, sizeof(gWPArray[gWPNum]) );
  424.     }
  425.  
  426.     //gWPArray[gWPNum] = NULL;
  427.  
  428.     if (gWPArray[gWPNum])
  429.     {
  430.         gWPArray[gWPNum]->inuse = 0;
  431.     }
  432. }
  433.  
  434. void RemoveWP_InTrail(int afterindex)
  435. {
  436.     int foundindex;
  437.     int foundanindex;
  438.     int didchange;
  439.     int i;
  440.  
  441.     foundindex = 0;
  442.     foundanindex = 0;
  443.     didchange = 0;
  444.     i = 0;
  445.  
  446.     if (afterindex < 0 || afterindex >= gWPNum)
  447.     {
  448.         Com_Printf(S_COLOR_YELLOW "Waypoint number %i does not exist\n", afterindex);
  449.         return;
  450.     }
  451.  
  452.     while (i < gWPNum)
  453.     {
  454.         if (gWPArray[i] && gWPArray[i]->inuse && gWPArray[i]->index == afterindex)
  455.         {
  456.             foundindex = i;
  457.             foundanindex = 1;
  458.             break;
  459.         }
  460.  
  461.         i++;
  462.     }
  463.  
  464.     if (!foundanindex)
  465.     {
  466.         Com_Printf(S_COLOR_YELLOW "Waypoint index %i should exist, but does not (?)\n", afterindex);
  467.         return;
  468.     }
  469.  
  470.     i = 0;
  471.  
  472.     while (i <= gWPNum)
  473.     {
  474.         if (gWPArray[i] && gWPArray[i]->index == foundindex)
  475.         {
  476.             //B_Free(gWPArray[i]);
  477.  
  478.             //Keep reusing the memory
  479.             memset( gWPArray[i], 0, sizeof(gWPArray[i]) );
  480.  
  481.             //gWPArray[i] = NULL;
  482.             gWPArray[i]->inuse = 0;
  483.             didchange = 1;
  484.         }
  485.         else if (gWPArray[i] && didchange)
  486.         {
  487.             TransferWPData(i, i-1);
  488.             //B_Free(gWPArray[i]);
  489.  
  490.             //Keep reusing the memory
  491.             memset( gWPArray[i], 0, sizeof(gWPArray[i]) );
  492.  
  493.             //gWPArray[i] = NULL;
  494.             gWPArray[i]->inuse = 0;
  495.         }
  496.  
  497.         i++;
  498.     }
  499.     gWPNum--;
  500. }
  501.  
  502. int CreateNewWP_InTrail(vec3_t origin, int flags, int afterindex)
  503. {
  504.     int foundindex;
  505.     int foundanindex;
  506.     int i;
  507.  
  508.     foundindex = 0;
  509.     foundanindex = 0;
  510.     i = 0;
  511.  
  512.     if (gWPNum >= MAX_WPARRAY_SIZE)
  513.     {
  514.         Com_Printf(S_COLOR_YELLOW "Warning: Waypoint limit hit (%i)\n", MAX_WPARRAY_SIZE);
  515.         return 0;
  516.     }
  517.  
  518.     if (afterindex < 0 || afterindex >= gWPNum)
  519.     {
  520.         Com_Printf(S_COLOR_YELLOW "Waypoint number %i does not exist\n", afterindex);
  521.         return 0;
  522.     }
  523.  
  524.     while (i < gWPNum)
  525.     {
  526.         if (gWPArray[i] && gWPArray[i]->inuse && gWPArray[i]->index == afterindex)
  527.         {
  528.             foundindex = i;
  529.             foundanindex = 1;
  530.             break;
  531.         }
  532.  
  533.         i++;
  534.     }
  535.  
  536.     if (!foundanindex)
  537.     {
  538.         Com_Printf(S_COLOR_YELLOW "Waypoint index %i should exist, but does not (?)\n", afterindex);
  539.         return 0;
  540.     }
  541.  
  542.     i = gWPNum;
  543.  
  544.     while (i >= 0)
  545.     {
  546.         if (gWPArray[i] && gWPArray[i]->inuse && gWPArray[i]->index != foundindex)
  547.         {
  548.             TransferWPData(i, i+1);
  549.         }
  550.         else if (gWPArray[i] && gWPArray[i]->inuse && gWPArray[i]->index == foundindex)
  551.         {
  552.             i++;
  553.  
  554.             if (!gWPArray[i])
  555.             {
  556.                 gWPArray[i] = (wpobject_t *)B_Alloc(sizeof(wpobject_t));
  557.             }
  558.  
  559.             gWPArray[i]->flags = flags;
  560.             gWPArray[i]->weight = 0; //calculated elsewhere
  561.             gWPArray[i]->associated_entity = ENTITYNUM_NONE; //set elsewhere
  562.             gWPArray[i]->disttonext = 0; //calculated elsewhere
  563.             gWPArray[i]->forceJumpTo = 0;
  564.             gWPArray[i]->index = i;
  565.             gWPArray[i]->inuse = 1;
  566.             VectorCopy(origin, gWPArray[i]->origin);
  567.             gWPNum++;
  568.             break;
  569.         }
  570.  
  571.         i--;
  572.     }
  573.  
  574.     return 1;
  575. }
  576.  
  577. void TeleportToWP(gentity_t *pl, int afterindex)
  578. {
  579.     int foundindex;
  580.     int foundanindex;
  581.     int i;
  582.  
  583.     if (!pl || !pl->client)
  584.     {
  585.         return;
  586.     }
  587.  
  588.     foundindex = 0;
  589.     foundanindex = 0;
  590.     i = 0;
  591.  
  592.     if (afterindex < 0 || afterindex >= gWPNum)
  593.     {
  594.         Com_Printf(S_COLOR_YELLOW "Waypoint number %i does not exist\n", afterindex);
  595.         return;
  596.     }
  597.  
  598.     while (i < gWPNum)
  599.     {
  600.         if (gWPArray[i] && gWPArray[i]->inuse && gWPArray[i]->index == afterindex)
  601.         {
  602.             foundindex = i;
  603.             foundanindex = 1;
  604.             break;
  605.         }
  606.  
  607.         i++;
  608.     }
  609.  
  610.     if (!foundanindex)
  611.     {
  612.         Com_Printf(S_COLOR_YELLOW "Waypoint index %i should exist, but does not (?)\n", afterindex);
  613.         return;
  614.     }
  615.  
  616.     VectorCopy(gWPArray[foundindex]->origin, pl->client->ps.origin);
  617.  
  618.     return;
  619. }
  620.  
  621. void WPFlagsModify(int wpnum, int flags)
  622. {
  623.     if (wpnum < 0 || wpnum >= gWPNum || !gWPArray[wpnum] || !gWPArray[wpnum]->inuse)
  624.     {
  625.         Com_Printf(S_COLOR_YELLOW "WPFlagsModify: Waypoint %i does not exist\n", wpnum);
  626.         return;
  627.     }
  628.  
  629.     gWPArray[wpnum]->flags = flags;
  630. }
  631.  
  632. int NotWithinRange(int base, int extent)
  633. {
  634.     if (extent > base && base+5 >= extent)
  635.     {
  636.         return 0;
  637.     }
  638.  
  639.     if (extent < base && base-5 <= extent)
  640.     {
  641.         return 0;
  642.     }
  643.  
  644.     return 1;
  645. }
  646.  
  647. int NodeHere(vec3_t spot)
  648. {
  649.     int i;
  650.  
  651.     i = 0;
  652.  
  653.     while (i < nodenum)
  654.     {
  655.         if ((int)nodetable[i].origin[0] == (int)spot[0] &&
  656.             (int)nodetable[i].origin[1] == (int)spot[1])
  657.         {
  658.             if ((int)nodetable[i].origin[2] == (int)spot[2] ||
  659.                 ((int)nodetable[i].origin[2] < (int)spot[2] && (int)nodetable[i].origin[2]+5 > (int)spot[2]) ||
  660.                 ((int)nodetable[i].origin[2] > (int)spot[2] && (int)nodetable[i].origin[2]-5 < (int)spot[2]))
  661.             {
  662.                 return 1;
  663.             }
  664.         }
  665.         i++;
  666.     }
  667.  
  668.     return 0;
  669. }
  670.  
  671. int CanGetToVector(vec3_t org1, vec3_t org2, vec3_t mins, vec3_t maxs)
  672. {
  673.     trace_t tr;
  674.  
  675.     trap_Trace(&tr, org1, mins, maxs, org2, -1, MASK_SOLID);
  676.  
  677.     if (tr.fraction == 1 && !tr.startsolid && !tr.allsolid)
  678.     {
  679.         return 1;
  680.     }
  681.  
  682.     return 0;
  683. }
  684.  
  685. int CanGetToVectorTravel(vec3_t org1, vec3_t org2, vec3_t mins, vec3_t maxs)
  686. {
  687.     trace_t tr;
  688.     vec3_t a, ang, fwd;
  689.     vec3_t midpos, dmid;
  690.     float startheight, midheight, fLen;
  691.  
  692.     mins[2] = -13;
  693.     maxs[2] = 13;
  694.  
  695.     trap_Trace(&tr, org1, mins, maxs, org2, -1, MASK_SOLID);
  696.  
  697.     if (tr.fraction != 1 || tr.startsolid || tr.allsolid)
  698.     {
  699.         return 0;
  700.     }
  701.  
  702.     VectorSubtract(org2, org1, a);
  703.  
  704.     vectoangles(a, ang);
  705.  
  706.     AngleVectors(ang, fwd, NULL, NULL);
  707.  
  708.     fLen = VectorLength(a)/2;
  709.  
  710.     midpos[0] = org1[0] + fwd[0]*fLen;
  711.     midpos[1] = org1[1] + fwd[1]*fLen;
  712.     midpos[2] = org1[2] + fwd[2]*fLen;
  713.  
  714.     VectorCopy(org1, dmid);
  715.     dmid[2] -= 1024;
  716.  
  717.     trap_Trace(&tr, midpos, NULL, NULL, dmid, -1, MASK_SOLID);
  718.  
  719.     startheight = org1[2] - tr.endpos[2];
  720.  
  721.     VectorCopy(midpos, dmid);
  722.     dmid[2] -= 1024;
  723.  
  724.     trap_Trace(&tr, midpos, NULL, NULL, dmid, -1, MASK_SOLID);
  725.  
  726.     if (tr.startsolid || tr.allsolid)
  727.     {
  728.         return 1;
  729.     }
  730.  
  731.     midheight = midpos[2] - tr.endpos[2];
  732.  
  733.     if (midheight > startheight*2)
  734.     {
  735.         return 0; //too steep of a drop.. can't go on
  736.     }
  737.  
  738.     return 1;
  739. }
  740.  
  741. int ConnectTrail(int startindex, int endindex)
  742. {
  743.     int foundit;
  744.     int cancontinue;
  745.     int i;
  746.     int failsafe;
  747.     int successnodeindex;
  748.     int insertindex;
  749.     int prenodestart;
  750.     int extendednodes[MAX_NODETABLE_SIZE]; //for storing checked nodes and not trying to extend them each a bazillion times
  751.     float fvecmeas;
  752.     float baseheight;
  753.     vec3_t a;
  754.     vec3_t startplace, starttrace;
  755.     vec3_t mins, maxs;
  756.     vec3_t testspot;
  757.     vec3_t validspotpos;
  758.     trace_t tr;
  759.  
  760.     mins[0] = -15;
  761.     mins[1] = -15;
  762.     mins[2] = 0;
  763.     maxs[0] = 15;
  764.     maxs[1] = 15;
  765.     maxs[2] = 0;
  766.  
  767.     nodenum = 0;
  768.     foundit = 0;
  769.  
  770.     i = 0;
  771.  
  772.     successnodeindex = 0;
  773.  
  774.     while (i < MAX_NODETABLE_SIZE) //clear it out before using it
  775.     {
  776.         nodetable[i].flags = 0;
  777.         nodetable[i].index = 0;
  778.         nodetable[i].inuse = 0;
  779.         nodetable[i].neighbornum = 0;
  780.         nodetable[i].origin[0] = 0;
  781.         nodetable[i].origin[1] = 0;
  782.         nodetable[i].origin[2] = 0;
  783.         nodetable[i].weight = 0;
  784.  
  785.         extendednodes[i] = 0;
  786.  
  787.         i++;
  788.     }
  789.  
  790.     i = 0;
  791.  
  792.     Com_Printf(S_COLOR_YELLOW "Point %i is not connected to %i - Repairing...\n", startindex, endindex);
  793.  
  794.     VectorCopy(gWPArray[startindex]->origin, startplace);
  795.  
  796.     VectorCopy(startplace, starttrace);
  797.  
  798.     starttrace[2] -= 4096;
  799.  
  800.     trap_Trace(&tr, startplace, NULL, NULL, starttrace, -1, MASK_SOLID);
  801.  
  802.     baseheight = startplace[2] - tr.endpos[2];
  803.  
  804.     cancontinue = 1;
  805.  
  806.     VectorCopy(startplace, nodetable[nodenum].origin);
  807.     nodetable[nodenum].weight = 1;
  808.     nodetable[nodenum].inuse = 1;
  809.     nodetable[nodenum].index = nodenum;
  810.     nodenum++;
  811.  
  812.     while (nodenum < MAX_NODETABLE_SIZE && !foundit && cancontinue)
  813.     {
  814.         cancontinue = 0;
  815.         i = 0;
  816.         prenodestart = nodenum;
  817.  
  818.         while (i < prenodestart)
  819.         {
  820.             if (extendednodes[i] != 1)
  821.             {
  822.                 VectorSubtract(gWPArray[endindex]->origin, nodetable[i].origin, a);
  823.                 fvecmeas = VectorLength(a);
  824.  
  825.                 if (fvecmeas < 128 && CanGetToVector(gWPArray[endindex]->origin, nodetable[i].origin, mins, maxs))
  826.                 {
  827.                     foundit = 1;
  828.                     successnodeindex = i;
  829.                     break;
  830.                 }
  831.  
  832.                 VectorCopy(nodetable[i].origin, testspot);
  833.                 testspot[0] += TABLE_BRANCH_DISTANCE;
  834.  
  835.                 VectorCopy(testspot, starttrace);
  836.  
  837.                 starttrace[2] -= 4096;
  838.  
  839.                 trap_Trace(&tr, testspot, NULL, NULL, starttrace, -1, MASK_SOLID);
  840.  
  841.                 testspot[2] = tr.endpos[2]+baseheight;
  842.  
  843.                 if (!NodeHere(testspot) && !tr.startsolid && !tr.allsolid && CanGetToVector(nodetable[i].origin, testspot, mins, maxs))
  844.                 {
  845.                     VectorCopy(testspot, nodetable[nodenum].origin);
  846.                     nodetable[nodenum].inuse = 1;
  847.                     nodetable[nodenum].index = nodenum;
  848.                     nodetable[nodenum].weight = nodetable[i].weight+1;
  849.                     nodetable[nodenum].neighbornum = i;
  850.                     if ((nodetable[i].origin[2] - nodetable[nodenum].origin[2]) > 50)
  851.                     { //if there's a big drop, make sure we know we can't just magically fly back up
  852.                         nodetable[nodenum].flags = WPFLAG_ONEWAY_FWD;
  853.                     }
  854.                     nodenum++;
  855.                     cancontinue = 1;
  856.                 }
  857.  
  858.                 if (nodenum >= MAX_NODETABLE_SIZE)
  859.                 {
  860.                     break; //failure
  861.                 }
  862.  
  863.                 VectorCopy(nodetable[i].origin, testspot);
  864.                 testspot[0] -= TABLE_BRANCH_DISTANCE;
  865.  
  866.                 VectorCopy(testspot, starttrace);
  867.  
  868.                 starttrace[2] -= 4096;
  869.  
  870.                 trap_Trace(&tr, testspot, NULL, NULL, starttrace, -1, MASK_SOLID);
  871.  
  872.                 testspot[2] = tr.endpos[2]+baseheight;
  873.  
  874.                 if (!NodeHere(testspot) && !tr.startsolid && !tr.allsolid && CanGetToVector(nodetable[i].origin, testspot, mins, maxs))
  875.                 {
  876.                     VectorCopy(testspot, nodetable[nodenum].origin);
  877.                     nodetable[nodenum].inuse = 1;
  878.                     nodetable[nodenum].index = nodenum;
  879.                     nodetable[nodenum].weight = nodetable[i].weight+1;
  880.                     nodetable[nodenum].neighbornum = i;
  881.                     if ((nodetable[i].origin[2] - nodetable[nodenum].origin[2]) > 50)
  882.                     { //if there's a big drop, make sure we know we can't just magically fly back up
  883.                         nodetable[nodenum].flags = WPFLAG_ONEWAY_FWD;
  884.                     }
  885.                     nodenum++;
  886.                     cancontinue = 1;
  887.                 }
  888.  
  889.                 if (nodenum >= MAX_NODETABLE_SIZE)
  890.                 {
  891.                     break; //failure
  892.                 }
  893.  
  894.                 VectorCopy(nodetable[i].origin, testspot);
  895.                 testspot[1] += TABLE_BRANCH_DISTANCE;
  896.  
  897.                 VectorCopy(testspot, starttrace);
  898.  
  899.                 starttrace[2] -= 4096;
  900.  
  901.                 trap_Trace(&tr, testspot, NULL, NULL, starttrace, -1, MASK_SOLID);
  902.  
  903.                 testspot[2] = tr.endpos[2]+baseheight;
  904.  
  905.                 if (!NodeHere(testspot) && !tr.startsolid && !tr.allsolid && CanGetToVector(nodetable[i].origin, testspot, mins, maxs))
  906.                 {
  907.                     VectorCopy(testspot, nodetable[nodenum].origin);
  908.                     nodetable[nodenum].inuse = 1;
  909.                     nodetable[nodenum].index = nodenum;
  910.                     nodetable[nodenum].weight = nodetable[i].weight+1;
  911.                     nodetable[nodenum].neighbornum = i;
  912.                     if ((nodetable[i].origin[2] - nodetable[nodenum].origin[2]) > 50)
  913.                     { //if there's a big drop, make sure we know we can't just magically fly back up
  914.                         nodetable[nodenum].flags = WPFLAG_ONEWAY_FWD;
  915.                     }
  916.                     nodenum++;
  917.                     cancontinue = 1;
  918.                 }
  919.  
  920.                 if (nodenum >= MAX_NODETABLE_SIZE)
  921.                 {
  922.                     break; //failure
  923.                 }
  924.  
  925.                 VectorCopy(nodetable[i].origin, testspot);
  926.                 testspot[1] -= TABLE_BRANCH_DISTANCE;
  927.  
  928.                 VectorCopy(testspot, starttrace);
  929.  
  930.                 starttrace[2] -= 4096;
  931.  
  932.                 trap_Trace(&tr, testspot, NULL, NULL, starttrace, -1, MASK_SOLID);
  933.  
  934.                 testspot[2] = tr.endpos[2]+baseheight;
  935.  
  936.                 if (!NodeHere(testspot) && !tr.startsolid && !tr.allsolid && CanGetToVector(nodetable[i].origin, testspot, mins, maxs))
  937.                 {
  938.                     VectorCopy(testspot, nodetable[nodenum].origin);
  939.                     nodetable[nodenum].inuse = 1;
  940.                     nodetable[nodenum].index = nodenum;
  941.                     nodetable[nodenum].weight = nodetable[i].weight+1;
  942.                     nodetable[nodenum].neighbornum = i;
  943.                     if ((nodetable[i].origin[2] - nodetable[nodenum].origin[2]) > 50)
  944.                     { //if there's a big drop, make sure we know we can't just magically fly back up
  945.                         nodetable[nodenum].flags = WPFLAG_ONEWAY_FWD;
  946.                     }
  947.                     nodenum++;
  948.                     cancontinue = 1;
  949.                 }
  950.  
  951.                 if (nodenum >= MAX_NODETABLE_SIZE)
  952.                 {
  953.                     break; //failure
  954.                 }
  955.  
  956.                 extendednodes[i] = 1;
  957.             }
  958.  
  959.             i++;
  960.         }
  961.     }
  962.  
  963.     if (!foundit)
  964.     {
  965.         Com_Printf(S_COLOR_RED "Could not link %i to %i, unreachable by node branching.\n", startindex, endindex);
  966.         gWPArray[startindex]->flags |= WPFLAG_ONEWAY_FWD;
  967.         gWPArray[endindex]->flags |= WPFLAG_ONEWAY_BACK;
  968.         Com_Printf(S_COLOR_YELLOW "Since points cannot be connected, point %i has been flagged as only-forward and point %i has been flagged as only-backward.\n", startindex, endindex);
  969.  
  970.         /*while (nodenum >= 0)
  971.         {
  972.             if (nodetable[nodenum].origin[0] || nodetable[nodenum].origin[1] || nodetable[nodenum].origin[2])
  973.             {
  974.                 CreateNewWP(nodetable[nodenum].origin, nodetable[nodenum].flags);
  975.             }
  976.  
  977.             nodenum--;
  978.         }*/
  979.         //The above code transfers nodes into the "rendered" waypoint array. Strictly for debugging.
  980.  
  981.         return 0;
  982.     }
  983.  
  984.     i = successnodeindex;
  985.     insertindex = startindex;
  986.     failsafe = 0;
  987.     VectorCopy(gWPArray[startindex]->origin, validspotpos);
  988.  
  989.     while (failsafe < MAX_NODETABLE_SIZE && i < MAX_NODETABLE_SIZE && i >= 0)
  990.     {
  991.         VectorSubtract(validspotpos, nodetable[i].origin, a);
  992.         if (!nodetable[nodetable[i].neighbornum].inuse || !CanGetToVectorTravel(validspotpos, /*nodetable[nodetable[i].neighbornum].origin*/nodetable[i].origin, mins, maxs) || VectorLength(a) > 256 || (!CanGetToVectorTravel(validspotpos, gWPArray[endindex]->origin, mins, maxs) && CanGetToVectorTravel(nodetable[i].origin, gWPArray[endindex]->origin, mins, maxs)) )
  993.         {
  994.             if (!CreateNewWP_InTrail(nodetable[i].origin, nodetable[i].flags, insertindex))
  995.             {
  996.                 Com_Printf(S_COLOR_RED "Could not link %i to %i, waypoint limit hit.\n", startindex, endindex);
  997.                 return 0;
  998.             }
  999.  
  1000.             VectorCopy(nodetable[i].origin, validspotpos);
  1001.         }
  1002.  
  1003.         if (i == 0)
  1004.         {
  1005.             break;
  1006.         }
  1007.  
  1008.         i = nodetable[i].neighbornum;
  1009.  
  1010.         failsafe++;
  1011.     }
  1012.  
  1013.     Com_Printf(S_COLOR_YELLOW "Finished connecting %i to %i.\n", startindex, endindex);
  1014.  
  1015.     return 1;
  1016. }
  1017.  
  1018. int OpposingEnds(int start, int end)
  1019. {
  1020.     if (!gWPArray[start] || !gWPArray[start]->inuse || !gWPArray[end] || !gWPArray[end]->inuse)
  1021.     {
  1022.         return 0;
  1023.     }
  1024.  
  1025.     if ((gWPArray[start]->flags & WPFLAG_ONEWAY_FWD) &&
  1026.         (gWPArray[end]->flags & WPFLAG_ONEWAY_BACK))
  1027.     {
  1028.         return 1;
  1029.     }
  1030.  
  1031.     return 0;
  1032. }
  1033.  
  1034. int DoorBlockingSection(int start, int end)
  1035. { //if a door blocks the trail, we'll just have to assume the points on each side are in visibility when it's open
  1036.     trace_t tr;
  1037.     gentity_t *testdoor;
  1038.     int start_trace_index;
  1039.  
  1040.     if (!gWPArray[start] || !gWPArray[start]->inuse || !gWPArray[end] || !gWPArray[end]->inuse)
  1041.     {
  1042.         return 0;
  1043.     }
  1044.  
  1045.     trap_Trace(&tr, gWPArray[start]->origin, NULL, NULL, gWPArray[end]->origin, -1, MASK_SOLID);
  1046.  
  1047.     if (tr.fraction == 1)
  1048.     {
  1049.         return 0;
  1050.     }
  1051.  
  1052.     testdoor = &g_entities[tr.entityNum];
  1053.  
  1054.     if (!testdoor)
  1055.     {
  1056.         return 0;
  1057.     }
  1058.  
  1059.     if (!strstr(testdoor->classname, "func_"))
  1060.     {
  1061.         return 0;
  1062.     }
  1063.  
  1064.     start_trace_index = tr.entityNum;
  1065.  
  1066.     trap_Trace(&tr, gWPArray[end]->origin, NULL, NULL, gWPArray[start]->origin, -1, MASK_SOLID);
  1067.  
  1068.     if (tr.fraction == 1)
  1069.     {
  1070.         return 0;
  1071.     }
  1072.  
  1073.     if (start_trace_index == tr.entityNum)
  1074.     {
  1075.         return 1;
  1076.     }
  1077.  
  1078.     return 0;
  1079. }
  1080.  
  1081. int RepairPaths(void)
  1082. {
  1083.     int i;
  1084.     int ctRet;
  1085.     vec3_t a;
  1086.  
  1087.     if (!gWPNum)
  1088.     {
  1089.         return 0;
  1090.     }
  1091.  
  1092.     i = 0;
  1093.  
  1094.     trap_Cvar_Update(&bot_wp_distconnect);
  1095.     trap_Cvar_Update(&bot_wp_visconnect);
  1096.  
  1097.     while (i < gWPNum)
  1098.     {
  1099.         if (gWPArray[i] && gWPArray[i]->inuse && gWPArray[i+1] && gWPArray[i+1]->inuse)
  1100.         {
  1101.             VectorSubtract(gWPArray[i]->origin, gWPArray[i+1]->origin, a);
  1102.  
  1103.             if (!(gWPArray[i+1]->flags & WPFLAG_NOVIS) &&
  1104.                 !(gWPArray[i+1]->flags & WPFLAG_JUMP) && //don't calculate on jump points because they might not always want to be visible (in cases of force jumping)
  1105.                 !OpposingEnds(i, i+1) &&
  1106.                 ((VectorLength(a) > 400 && bot_wp_distconnect.value) || (!OrgVisible(gWPArray[i]->origin, gWPArray[i+1]->origin, -1) && bot_wp_visconnect.value) ) &&
  1107.                 !DoorBlockingSection(i, i+1))
  1108.             {
  1109.                 ctRet = ConnectTrail(i, i+1);
  1110.                 /*if (!ctRet)
  1111.                 {
  1112.                     return 0;
  1113.                 }*/ //we still want to write it..
  1114.             }
  1115.         }
  1116.  
  1117.         i++;
  1118.     }
  1119.  
  1120.     return 1;
  1121. }
  1122.  
  1123. int OrgVisibleCurve(vec3_t org1, vec3_t mins, vec3_t maxs, vec3_t org2, int ignore)
  1124. {
  1125.     trace_t tr;
  1126.     vec3_t evenorg1;
  1127.  
  1128.     VectorCopy(org1, evenorg1);
  1129.     evenorg1[2] = org2[2];
  1130.  
  1131.     trap_Trace(&tr, evenorg1, mins, maxs, org2, ignore, MASK_SOLID);
  1132.  
  1133.     if (tr.fraction == 1 && !tr.startsolid && !tr.allsolid)
  1134.     {
  1135.         trap_Trace(&tr, evenorg1, mins, maxs, org1, ignore, MASK_SOLID);
  1136.  
  1137.         if (tr.fraction == 1 && !tr.startsolid && !tr.allsolid)
  1138.         {
  1139.             return 1;
  1140.         }
  1141.     }
  1142.  
  1143.     return 0;
  1144. }
  1145.  
  1146. int CanForceJumpTo(int baseindex, int testingindex, float distance)
  1147. {
  1148. #if 0
  1149.     float heightdif;
  1150.     vec3_t xy_base, xy_test, v, mins, maxs;
  1151.     wpobject_t *wpBase = gWPArray[baseindex];
  1152.     wpobject_t *wpTest = gWPArray[testingindex];
  1153.  
  1154.     mins[0] = -15;
  1155.     mins[1] = -15;
  1156.     mins[2] = -15; //-1
  1157.     maxs[0] = 15;
  1158.     maxs[1] = 15;
  1159.     maxs[2] = 15; //1
  1160.  
  1161.     if (!wpBase || !wpBase->inuse || !wpTest || !wpTest->inuse)
  1162.     {
  1163.         return 0;
  1164.     }
  1165.  
  1166.     if (distance > 400)
  1167.     {
  1168.         return 0;
  1169.     }
  1170.  
  1171.     VectorCopy(wpBase->origin, xy_base);
  1172.     VectorCopy(wpTest->origin, xy_test);
  1173.  
  1174.     xy_base[2] = xy_test[2];
  1175.  
  1176.     VectorSubtract(xy_base, xy_test, v);
  1177.  
  1178.     if (VectorLength(v) > MAX_NEIGHBOR_LINK_DISTANCE)
  1179.     {
  1180.         return 0;
  1181.     }
  1182.  
  1183.     if ((int)wpBase->origin[2] < (int)wpTest->origin[2])
  1184.     {
  1185.         heightdif = wpTest->origin[2] - wpBase->origin[2];
  1186.     }
  1187.     else
  1188.     {
  1189.         return 0; //err..
  1190.     }
  1191.  
  1192.     if (heightdif < 128)
  1193.     { //don't bother..
  1194.         return 0;
  1195.     }
  1196.  
  1197.     if (heightdif > 512)
  1198.     { //too high
  1199.         return 0;
  1200.     }
  1201.  
  1202.     if (!OrgVisibleCurve(wpBase->origin, mins, maxs, wpTest->origin, -1))
  1203.     {
  1204.         return 0;
  1205.     }
  1206.  
  1207.     if (heightdif > 400)
  1208.     {
  1209.         return 3;
  1210.     }
  1211.     else if (heightdif > 256)
  1212.     {
  1213.         return 2;
  1214.     }
  1215.     else
  1216.     {
  1217.         return 1;
  1218.     }
  1219. #else
  1220.     return 0;
  1221. #endif
  1222. }
  1223.  
  1224. void CalculatePaths(void)
  1225. {
  1226.     int i;
  1227.     int c;
  1228.     int forceJumpable;
  1229.     float nLDist;
  1230.     vec3_t a;
  1231.     vec3_t mins, maxs;
  1232.  
  1233.     if (!gWPNum)
  1234.     {
  1235.         return;
  1236.     }
  1237.  
  1238.     mins[0] = -15;
  1239.     mins[1] = -15;
  1240.     mins[2] = -15; //-1
  1241.     maxs[0] = 15;
  1242.     maxs[1] = 15;
  1243.     maxs[2] = 15; //1
  1244.  
  1245.     //now clear out all the neighbor data before we recalculate
  1246.     i = 0;
  1247.  
  1248.     while (i < gWPNum)
  1249.     {
  1250.         if (gWPArray[i] && gWPArray[i]->inuse && gWPArray[i]->neighbornum)
  1251.         {
  1252.             while (gWPArray[i]->neighbornum >= 0)
  1253.             {
  1254.                 gWPArray[i]->neighbors[gWPArray[i]->neighbornum].num = 0;
  1255.                 gWPArray[i]->neighbors[gWPArray[i]->neighbornum].forceJumpTo = 0;
  1256.                 gWPArray[i]->neighbornum--;
  1257.             }
  1258.             gWPArray[i]->neighbornum = 0;
  1259.         }
  1260.  
  1261.         i++;
  1262.     }
  1263.  
  1264.     i = 0;
  1265.  
  1266.     while (i < gWPNum)
  1267.     {
  1268.         if (gWPArray[i] && gWPArray[i]->inuse)
  1269.         {
  1270.             c = 0;
  1271.  
  1272.             while (c < gWPNum)
  1273.             {
  1274.                 if (gWPArray[c] && gWPArray[c]->inuse && i != c &&
  1275.                     NotWithinRange(i, c))
  1276.                 {
  1277.                     VectorSubtract(gWPArray[i]->origin, gWPArray[c]->origin, a);
  1278.  
  1279.                     nLDist = VectorLength(a);
  1280.                     forceJumpable = CanForceJumpTo(i, c, nLDist);
  1281.  
  1282.                     if ((nLDist < MAX_NEIGHBOR_LINK_DISTANCE || forceJumpable) &&
  1283.                         ((int)gWPArray[i]->origin[2] == (int)gWPArray[c]->origin[2] || forceJumpable) &&
  1284.                         (OrgVisibleBox(gWPArray[i]->origin, mins, maxs, gWPArray[c]->origin, -1) || forceJumpable))
  1285.                     {
  1286.                         gWPArray[i]->neighbors[gWPArray[i]->neighbornum].num = c;
  1287.                         if (forceJumpable && ((int)gWPArray[i]->origin[2] != (int)gWPArray[c]->origin[2] || nLDist < MAX_NEIGHBOR_LINK_DISTANCE))
  1288.                         {
  1289.                             gWPArray[i]->neighbors[gWPArray[i]->neighbornum].forceJumpTo = 999;//forceJumpable; //FJSR
  1290.                         }
  1291.                         else
  1292.                         {
  1293.                             gWPArray[i]->neighbors[gWPArray[i]->neighbornum].forceJumpTo = 0;
  1294.                         }
  1295.                         gWPArray[i]->neighbornum++;
  1296.                     }
  1297.  
  1298.                     if (gWPArray[i]->neighbornum >= MAX_NEIGHBOR_SIZE)
  1299.                     {
  1300.                         break;
  1301.                     }
  1302.                 }
  1303.                 c++;
  1304.             }
  1305.         }
  1306.         i++;
  1307.     }
  1308. }
  1309.  
  1310. gentity_t *GetObjectThatTargets(gentity_t *ent)
  1311. {
  1312.     gentity_t *next = NULL;
  1313.  
  1314.     if (!ent->targetname)
  1315.     {
  1316.         return NULL;
  1317.     }
  1318.  
  1319.     next = G_Find( next, FOFS(target), ent->targetname );
  1320.  
  1321.     if (next)
  1322.     {
  1323.         return next;
  1324.     }
  1325.  
  1326.     return NULL;
  1327. }
  1328.  
  1329. float botGlobalNavWeaponWeights[WP_NUM_WEAPONS] =
  1330. {
  1331.     0,//WP_NONE,
  1332.  
  1333.     0,//WP_KNIFE,
  1334.     0,//WP_M1911A1_PISTOL,
  1335.     1,//WP_USSOCOM_PISTOL,
  1336.     6,//WP_USAS_12_SHOTGUN,
  1337.     7,//WP_M590_SHOTGUN,
  1338.  
  1339.     8,//WP_MICRO_UZI_SUBMACHINEGUN,
  1340.     8,//WP_M3A1_SUBMACHINEGUN,
  1341.     9,//WP_M4_ASSAULT_RIFLE,
  1342.     9,//WP_AK74_ASSAULT_RIFLE,
  1343.  
  1344.     8,//WP_MSG90A1,
  1345.     9,//WP_M60_MACHINEGUN,
  1346.     7,//WP_MM1_GRENADE_LAUNCHER,
  1347.     7,//WP_RPG7_LAUNCHER,
  1348.  
  1349.     6,//WP_M84_GRENADE,
  1350.     6,//WP_SMOHG92_GRENADE,
  1351.  
  1352.     6,//WP_ANM14_GRENADE,
  1353.     6,//WP_M15_GRENADE,
  1354.  
  1355.     6,//WP_MP5
  1356. };
  1357.  
  1358. int GetNearestVisibleWPToItem(vec3_t org, int ignore)
  1359. {
  1360.     int i;
  1361.     float bestdist;
  1362.     float flLen;
  1363.     int bestindex;
  1364.     vec3_t a, mins, maxs;
  1365.  
  1366.     i = 0;
  1367.     bestdist = 64; //has to be less than 64 units to the item or it isn't safe enough
  1368.     bestindex = -1;
  1369.  
  1370.     mins[0] = -15;
  1371.     mins[1] = -15;
  1372.     mins[2] = 0;
  1373.     maxs[0] = 15;
  1374.     maxs[1] = 15;
  1375.     maxs[2] = 0;
  1376.  
  1377.     while (i < gWPNum)
  1378.     {
  1379.         if (gWPArray[i] && gWPArray[i]->inuse &&
  1380.             gWPArray[i]->origin[2]-15 < org[2] &&
  1381.             gWPArray[i]->origin[2]+15 > org[2])
  1382.         {
  1383.             VectorSubtract(org, gWPArray[i]->origin, a);
  1384.             flLen = VectorLength(a);
  1385.  
  1386.             if (flLen < bestdist && trap_InPVS(org, gWPArray[i]->origin) && OrgVisibleBox(org, mins, maxs, gWPArray[i]->origin, ignore))
  1387.             {
  1388.                 bestdist = flLen;
  1389.                 bestindex = i;
  1390.             }
  1391.         }
  1392.  
  1393.         i++;
  1394.     }
  1395.  
  1396.     return bestindex;
  1397. }
  1398.  
  1399. void CalculateWeightGoals(void)
  1400. { //set waypoint weights depending on weapon and item placement
  1401.     int i = 0;
  1402.     int wpindex = 0;
  1403.     gentity_t *ent;
  1404.     float weight;
  1405.  
  1406.     trap_Cvar_Update(&bot_wp_clearweight);
  1407.  
  1408.     if (bot_wp_clearweight.integer)
  1409.     { //if set then flush out all weight/goal values before calculating them again
  1410.         while (i < gWPNum)
  1411.         {
  1412.             if (gWPArray[i] && gWPArray[i]->inuse)
  1413.             {
  1414.                 gWPArray[i]->weight = 0;
  1415.  
  1416.                 if (gWPArray[i]->flags & WPFLAG_GOALPOINT)
  1417.                 {
  1418.                     gWPArray[i]->flags -= WPFLAG_GOALPOINT;
  1419.                 }
  1420.             }
  1421.  
  1422.             i++;
  1423.         }
  1424.     }
  1425.  
  1426.     i = 0;
  1427.  
  1428.     while (i < MAX_GENTITIES)
  1429.     {
  1430.         ent = &g_entities[i];
  1431.  
  1432.         weight = 0;
  1433.  
  1434.         if (ent && ent->classname)
  1435.         {
  1436.             if (strcmp(ent->classname, "pickup_armor_big") == 0)
  1437.             {
  1438.                 weight = 3;
  1439.             }
  1440.             else if (strcmp(ent->classname, "pickup_armor_medium") == 0)
  1441.             {
  1442.                 weight = 2;
  1443.             }
  1444.             else if (strcmp(ent->classname, "pickup_armor_small") == 0)
  1445.             {
  1446.                 weight = 1;
  1447.             }
  1448.             else if (strcmp(ent->classname, "pickup_health_big") == 0)
  1449.             {
  1450.                 weight = 2;
  1451.             }
  1452.             else if (strcmp(ent->classname, "pickup_health_small") == 0)
  1453.             {
  1454.                 weight = 1;
  1455.             }
  1456.             else if (strstr(ent->classname, "pickup_weapon_") && ent->item)
  1457.             {
  1458.                 weight = botGlobalNavWeaponWeights[ent->item->giTag];
  1459.             }
  1460.             else if (ent->item && ent->item->giType == IT_AMMO)
  1461.             {
  1462.                 weight = 3;
  1463.             }
  1464.         }
  1465.  
  1466.         if (ent && weight)
  1467.         {
  1468.             wpindex = GetNearestVisibleWPToItem(ent->s.pos.trBase, ent->s.number);
  1469.  
  1470.             if (wpindex != -1 && gWPArray[wpindex] && gWPArray[wpindex]->inuse)
  1471.             { //found the waypoint nearest the center of this object
  1472.                 gWPArray[wpindex]->weight = weight;
  1473.                 gWPArray[wpindex]->flags |= WPFLAG_GOALPOINT;
  1474.                 gWPArray[wpindex]->associated_entity = ent->s.number;
  1475.             }
  1476.         }
  1477.  
  1478.         i++;
  1479.     }
  1480. }
  1481.  
  1482. void CalculateJumpRoutes(void)
  1483. {
  1484.     int i = 0;
  1485.     float nheightdif = 0;
  1486.     float pheightdif = 0;
  1487.  
  1488.     while (i < gWPNum)
  1489.     {
  1490.         if (gWPArray[i] && gWPArray[i]->inuse)
  1491.         {
  1492.             if (gWPArray[i]->flags & WPFLAG_JUMP)
  1493.             {
  1494.                 nheightdif = 0;
  1495.                 pheightdif = 0;
  1496.  
  1497.                 gWPArray[i]->forceJumpTo = 0;
  1498.  
  1499.                 if (gWPArray[i-1] && gWPArray[i-1]->inuse && (gWPArray[i-1]->origin[2]+16) < gWPArray[i]->origin[2])
  1500.                 {
  1501.                     nheightdif = (gWPArray[i]->origin[2] - gWPArray[i-1]->origin[2]);
  1502.                 }
  1503.  
  1504.                 if (gWPArray[i+1] && gWPArray[i+1]->inuse && (gWPArray[i+1]->origin[2]+16) < gWPArray[i]->origin[2])
  1505.                 {
  1506.                     pheightdif = (gWPArray[i]->origin[2] - gWPArray[i+1]->origin[2]);
  1507.                 }
  1508.  
  1509.                 if (nheightdif > pheightdif)
  1510.                 {
  1511.                     pheightdif = nheightdif;
  1512.                 }
  1513.  
  1514.                 if (pheightdif)
  1515.                 {
  1516.                     if (pheightdif > 500)
  1517.                     {
  1518.                         gWPArray[i]->forceJumpTo = 999; //FORCE_LEVEL_3; //FJSR
  1519.                     }
  1520.                     else if (pheightdif > 256)
  1521.                     {
  1522.                         gWPArray[i]->forceJumpTo = 999; //FORCE_LEVEL_2; //FJSR
  1523.                     }
  1524.                     else if (pheightdif > 128)
  1525.                     {
  1526.                         gWPArray[i]->forceJumpTo = 999; //FORCE_LEVEL_1; //FJSR
  1527.                     }
  1528.                 }
  1529.             }
  1530.         }
  1531.  
  1532.         i++;
  1533.     }
  1534. }
  1535.  
  1536. int LoadPathData(const char *filename)
  1537. {
  1538.     fileHandle_t f;
  1539.     char *fileString;
  1540.     char *currentVar;
  1541.     char *routePath;
  1542.     wpobject_t thiswp;
  1543.     int len;
  1544.     int i, i_cv;
  1545.     int nei_num;
  1546.  
  1547.     i = 0;
  1548.     i_cv = 0;
  1549.  
  1550.     routePath = (char *)B_TempAlloc(1024);
  1551.  
  1552.     Com_sprintf(routePath, 1024, "botroutes/%s.wnt\0", filename);
  1553.  
  1554.     len = trap_FS_FOpenFile(routePath, &f, FS_READ);
  1555.  
  1556.     B_TempFree(1024); //routePath
  1557.  
  1558.     if (!f)
  1559.     {
  1560.         Com_Printf(S_COLOR_YELLOW "Bot route data not found\n");
  1561.         return 2;
  1562.     }
  1563.  
  1564.     if (len >= 524288)
  1565.     {
  1566.         Com_Printf(S_COLOR_RED "Route file exceeds maximum length\n");
  1567.         return 0;
  1568.     }
  1569.  
  1570.     fileString = (char *)B_TempAlloc(524288);
  1571.     currentVar = (char *)B_TempAlloc(2048);
  1572.  
  1573.     trap_FS_Read(fileString, len, f);
  1574.  
  1575.     while (i < len)
  1576.     {
  1577.         i_cv = 0;
  1578.  
  1579.         thiswp.index = 0;
  1580.         thiswp.flags = 0;
  1581.         thiswp.inuse = 0;
  1582.         thiswp.neighbornum = 0;
  1583.         thiswp.origin[0] = 0;
  1584.         thiswp.origin[1] = 0;
  1585.         thiswp.origin[2] = 0;
  1586.         thiswp.weight = 0;
  1587.         thiswp.associated_entity = ENTITYNUM_NONE;
  1588.         thiswp.forceJumpTo = 0;
  1589.         thiswp.disttonext = 0;
  1590.         nei_num = 0;
  1591.  
  1592.         while (nei_num < MAX_NEIGHBOR_SIZE)
  1593.         {
  1594.             thiswp.neighbors[nei_num].num = 0;
  1595.             thiswp.neighbors[nei_num].forceJumpTo = 0;
  1596.  
  1597.             nei_num++;
  1598.         }
  1599.         
  1600.         while (fileString[i] != ' ')
  1601.         {
  1602.             currentVar[i_cv] = fileString[i];
  1603.             i_cv++;
  1604.             i++;
  1605.         }
  1606.         currentVar[i_cv] = '\0';
  1607.  
  1608.         thiswp.index = atoi(currentVar);
  1609.  
  1610.         i_cv = 0;
  1611.         i++;
  1612.  
  1613.         while (fileString[i] != ' ')
  1614.         {
  1615.             currentVar[i_cv] = fileString[i];
  1616.             i_cv++;
  1617.             i++;
  1618.         }
  1619.         currentVar[i_cv] = '\0';
  1620.  
  1621.         thiswp.flags = atoi(currentVar);
  1622.  
  1623.         i_cv = 0;
  1624.         i++;
  1625.  
  1626.         while (fileString[i] != ' ')
  1627.         {
  1628.             currentVar[i_cv] = fileString[i];
  1629.             i_cv++;
  1630.             i++;
  1631.         }
  1632.         currentVar[i_cv] = '\0';
  1633.  
  1634.         thiswp.weight = atof(currentVar);
  1635.  
  1636.         i_cv = 0;
  1637.         i++;
  1638.         i++;
  1639.  
  1640.         while (fileString[i] != ' ')
  1641.         {
  1642.             currentVar[i_cv] = fileString[i];
  1643.             i_cv++;
  1644.             i++;
  1645.         }
  1646.         currentVar[i_cv] = '\0';
  1647.  
  1648.         thiswp.origin[0] = atof(currentVar);
  1649.  
  1650.         i_cv = 0;
  1651.         i++;
  1652.  
  1653.         while (fileString[i] != ' ')
  1654.         {
  1655.             currentVar[i_cv] = fileString[i];
  1656.             i_cv++;
  1657.             i++;
  1658.         }
  1659.         currentVar[i_cv] = '\0';
  1660.  
  1661.         thiswp.origin[1] = atof(currentVar);
  1662.  
  1663.         i_cv = 0;
  1664.         i++;
  1665.  
  1666.         while (fileString[i] != ')')
  1667.         {
  1668.             currentVar[i_cv] = fileString[i];
  1669.             i_cv++;
  1670.             i++;
  1671.         }
  1672.         currentVar[i_cv] = '\0';
  1673.  
  1674.         thiswp.origin[2] = atof(currentVar);
  1675.  
  1676.         i += 4;
  1677.  
  1678.         while (fileString[i] != '}')
  1679.         {
  1680.             i_cv = 0;
  1681.             while (fileString[i] != ' ' && fileString[i] != '-')
  1682.             {
  1683.                 currentVar[i_cv] = fileString[i];
  1684.                 i_cv++;
  1685.                 i++;
  1686.             }
  1687.             currentVar[i_cv] = '\0';
  1688.  
  1689.             thiswp.neighbors[thiswp.neighbornum].num = atoi(currentVar);
  1690.  
  1691.             if (fileString[i] == '-')
  1692.             {
  1693.                 i_cv = 0;
  1694.                 i++;
  1695.  
  1696.                 while (fileString[i] != ' ')
  1697.                 {
  1698.                     currentVar[i_cv] = fileString[i];
  1699.                     i_cv++;
  1700.                     i++;
  1701.                 }
  1702.                 currentVar[i_cv] = '\0';
  1703.  
  1704.                 thiswp.neighbors[thiswp.neighbornum].forceJumpTo = 999; //atoi(currentVar); //FJSR
  1705.             }
  1706.             else
  1707.             {
  1708.                 thiswp.neighbors[thiswp.neighbornum].forceJumpTo = 0;
  1709.             }
  1710.  
  1711.             thiswp.neighbornum++;
  1712.  
  1713.             i++;
  1714.         }
  1715.  
  1716.         i_cv = 0;
  1717.         i++;
  1718.         i++;
  1719.  
  1720.         while (fileString[i] != '\n')
  1721.         {
  1722.             currentVar[i_cv] = fileString[i];
  1723.             i_cv++;
  1724.             i++;
  1725.         }
  1726.         currentVar[i_cv] = '\0';
  1727.  
  1728.         thiswp.disttonext = atof(currentVar);
  1729.  
  1730.         CreateNewWP_FromObject(&thiswp);
  1731.         i++;
  1732.     }
  1733.  
  1734.     B_TempFree(524288); //fileString
  1735.     B_TempFree(2048); //currentVar
  1736.  
  1737.     trap_FS_FCloseFile(f);
  1738.  
  1739.     CalculateWeightGoals();
  1740.     //calculate weights for idle activity goals when
  1741.     //the bot has absolutely nothing else to do
  1742.  
  1743.     CalculateJumpRoutes();
  1744.     //Look at jump points and mark them as requiring
  1745.     //force jumping as needed
  1746.  
  1747.     return 1;
  1748. }
  1749.  
  1750. void FlagObjects(void)
  1751. {
  1752.     int i = 0, bestindex = 0, found = 0;
  1753.     float bestdist = 999999, tlen = 0;
  1754.     gentity_t *flag_red, *flag_blue, *ent;
  1755.     vec3_t a, mins, maxs;
  1756.     trace_t tr;
  1757.  
  1758.     flag_red = NULL;
  1759.     flag_blue = NULL;
  1760.  
  1761.     mins[0] = -15;
  1762.     mins[1] = -15;
  1763.     mins[2] = -5;
  1764.     maxs[0] = 15;
  1765.     maxs[1] = 15;
  1766.     maxs[2] = 5;
  1767.  
  1768.     while (i < MAX_GENTITIES)
  1769.     {
  1770.         ent = &g_entities[i];
  1771.  
  1772.         if (ent && ent->inuse && ent->classname)
  1773.         {
  1774.             if (!flag_red && strcmp(ent->classname, "team_CTF_redflag") == 0)
  1775.             {
  1776.                 flag_red = ent;
  1777.             }
  1778.             else if (!flag_blue && strcmp(ent->classname, "team_CTF_blueflag") == 0)
  1779.             {
  1780.                 flag_blue = ent;
  1781.             }
  1782.  
  1783.             if (flag_red && flag_blue)
  1784.             {
  1785.                 break;
  1786.             }
  1787.         }
  1788.  
  1789.         i++;
  1790.     }
  1791.  
  1792.     i = 0;
  1793.  
  1794.     if (!flag_red || !flag_blue)
  1795.     {
  1796.         return;
  1797.     }
  1798.  
  1799.     while (i < gWPNum)
  1800.     {
  1801.         if (gWPArray[i] && gWPArray[i]->inuse)
  1802.         {
  1803.             VectorSubtract(flag_red->s.pos.trBase, gWPArray[i]->origin, a);
  1804.             tlen = VectorLength(a);
  1805.  
  1806.             if (tlen < bestdist)
  1807.             {
  1808.                 trap_Trace(&tr, flag_red->s.pos.trBase, mins, maxs, gWPArray[i]->origin, flag_red->s.number, MASK_SOLID);
  1809.  
  1810.                 if (tr.fraction == 1 || tr.entityNum == flag_red->s.number)
  1811.                 {
  1812.                     bestdist = tlen;
  1813.                     bestindex = i;
  1814.                     found = 1;
  1815.                 }
  1816.             }
  1817.  
  1818.         }
  1819.  
  1820.         i++;
  1821.     }
  1822.  
  1823.     if (found)
  1824.     {
  1825.         gWPArray[bestindex]->flags |= WPFLAG_RED_FLAG;
  1826.         flagRed = gWPArray[bestindex];
  1827.         oFlagRed = flagRed;
  1828.         eFlagRed = flag_red;
  1829.     }
  1830.  
  1831.     bestdist = 999999;
  1832.     bestindex = 0;
  1833.     found = 0;
  1834.     i = 0;
  1835.  
  1836.     while (i < gWPNum)
  1837.     {
  1838.         if (gWPArray[i] && gWPArray[i]->inuse)
  1839.         {
  1840.             VectorSubtract(flag_blue->s.pos.trBase, gWPArray[i]->origin, a);
  1841.             tlen = VectorLength(a);
  1842.  
  1843.             if (tlen < bestdist)
  1844.             {
  1845.                 trap_Trace(&tr, flag_blue->s.pos.trBase, mins, maxs, gWPArray[i]->origin, flag_blue->s.number, MASK_SOLID);
  1846.  
  1847.                 if (tr.fraction == 1 || tr.entityNum == flag_blue->s.number)
  1848.                 {
  1849.                     bestdist = tlen;
  1850.                     bestindex = i;
  1851.                     found = 1;
  1852.                 }
  1853.             }
  1854.  
  1855.         }
  1856.  
  1857.         i++;
  1858.     }
  1859.  
  1860.     if (found)
  1861.     {
  1862.         gWPArray[bestindex]->flags |= WPFLAG_BLUE_FLAG;
  1863.         flagBlue = gWPArray[bestindex];
  1864.         oFlagBlue = flagBlue;
  1865.         eFlagBlue = flag_blue;
  1866.     }
  1867. }
  1868.  
  1869. int SavePathData(const char *filename)
  1870. {
  1871.     fileHandle_t f;
  1872.     char *fileString;
  1873.     char *storeString;
  1874.     char *routePath;
  1875.     vec3_t a;
  1876.     float flLen;
  1877.     int i, s, n;
  1878.  
  1879.     fileString = NULL;
  1880.     i = 0;
  1881.     s = 0;
  1882.  
  1883.     if (!gWPNum)
  1884.     {
  1885.         return 0;
  1886.     }
  1887.  
  1888.     routePath = (char *)B_TempAlloc(1024);
  1889.  
  1890.     Com_sprintf(routePath, 1024, "botroutes/%s.wnt\0", filename);
  1891.  
  1892.     trap_FS_FOpenFile(routePath, &f, FS_WRITE);
  1893.  
  1894.     B_TempFree(1024); //routePath
  1895.  
  1896.     if (!f)
  1897.     {
  1898.         Com_Printf(S_COLOR_RED "ERROR: Could not open file to write path data\n");
  1899.         return 0;
  1900.     }
  1901.  
  1902.     if (!RepairPaths()) //check if we can see all waypoints from the last. If not, try to branch over.
  1903.     {
  1904.         trap_FS_FCloseFile(f);
  1905.         return 0;
  1906.     }
  1907.  
  1908.     CalculatePaths(); //make everything nice and connected before saving
  1909.  
  1910.     FlagObjects(); //currently only used for flagging waypoints nearest CTF flags
  1911.  
  1912.     fileString = (char *)B_TempAlloc(524288);
  1913.     storeString = (char *)B_TempAlloc(4096);
  1914.  
  1915.     Com_sprintf(fileString, 524288, "%i %i %f (%f %f %f) { ", gWPArray[i]->index, gWPArray[i]->flags, gWPArray[i]->weight, gWPArray[i]->origin[0], gWPArray[i]->origin[1], gWPArray[i]->origin[2]);
  1916.  
  1917.     n = 0;
  1918.  
  1919.     while (n < gWPArray[i]->neighbornum)
  1920.     {
  1921.         if (gWPArray[i]->neighbors[n].forceJumpTo)
  1922.         {
  1923.             Com_sprintf(storeString, 4096, "%s%i-%i ", storeString, gWPArray[i]->neighbors[n].num, gWPArray[i]->neighbors[n].forceJumpTo);
  1924.         }
  1925.         else
  1926.         {
  1927.             Com_sprintf(storeString, 4096, "%s%i ", storeString, gWPArray[i]->neighbors[n].num);
  1928.         }
  1929.         n++;
  1930.     }
  1931.  
  1932.     if (gWPArray[i+1] && gWPArray[i+1]->inuse && gWPArray[i+1]->index)
  1933.     {
  1934.         VectorSubtract(gWPArray[i]->origin, gWPArray[i+1]->origin, a);
  1935.         flLen = VectorLength(a);
  1936.     }
  1937.     else
  1938.     {
  1939.         flLen = 0;
  1940.     }
  1941.  
  1942.     gWPArray[i]->disttonext = flLen;
  1943.  
  1944.     Com_sprintf(fileString, 524288, "%s} %f\n", fileString, flLen);
  1945.  
  1946.     i++;
  1947.  
  1948.     while (i < gWPNum)
  1949.     {
  1950.         //sprintf(fileString, "%s%i %i %f (%f %f %f) { ", fileString, gWPArray[i]->index, gWPArray[i]->flags, gWPArray[i]->weight, gWPArray[i]->origin[0], gWPArray[i]->origin[1], gWPArray[i]->origin[2]);
  1951.         Com_sprintf(storeString, 4096, "%i %i %f (%f %f %f) { ", gWPArray[i]->index, gWPArray[i]->flags, gWPArray[i]->weight, gWPArray[i]->origin[0], gWPArray[i]->origin[1], gWPArray[i]->origin[2]);
  1952.  
  1953.         n = 0;
  1954.  
  1955.         while (n < gWPArray[i]->neighbornum)
  1956.         {
  1957.             if (gWPArray[i]->neighbors[n].forceJumpTo)
  1958.             {
  1959.                 Com_sprintf(storeString, 4096, "%s%i-%i ", storeString, gWPArray[i]->neighbors[n].num, gWPArray[i]->neighbors[n].forceJumpTo);
  1960.             }
  1961.             else
  1962.             {
  1963.                 Com_sprintf(storeString, 4096, "%s%i ", storeString, gWPArray[i]->neighbors[n].num);
  1964.             }
  1965.             n++;
  1966.         }
  1967.  
  1968.         if (gWPArray[i+1] && gWPArray[i+1]->inuse && gWPArray[i+1]->index)
  1969.         {
  1970.             VectorSubtract(gWPArray[i]->origin, gWPArray[i+1]->origin, a);
  1971.             flLen = VectorLength(a);
  1972.         }
  1973.         else
  1974.         {
  1975.             flLen = 0;
  1976.         }
  1977.  
  1978.         gWPArray[i]->disttonext = flLen;
  1979.  
  1980.         Com_sprintf(storeString, 4096, "%s} %f\n", storeString, flLen);
  1981.  
  1982.         strcat(fileString, storeString);
  1983.  
  1984.         i++;
  1985.     }
  1986.  
  1987.     trap_FS_Write(fileString, strlen(fileString), f);
  1988.  
  1989.     B_TempFree(524288); //fileString
  1990.     B_TempFree(4096); //storeString
  1991.  
  1992.     trap_FS_FCloseFile(f);
  1993.  
  1994.     Com_Printf("Path data has been saved and updated. You may need to restart the level for some things to be properly calculated.\n");
  1995.  
  1996.     return 1;
  1997. }
  1998.  
  1999. void LoadPath_ThisLevel(void)
  2000. {
  2001.     vmCvar_t    mapname;
  2002.     int            i = 0;
  2003.     gentity_t    *ent = NULL;
  2004.  
  2005.     trap_Cvar_Register( &mapname, "mapname", "", CVAR_SERVERINFO | CVAR_ROM, 0.0, 0.0 );
  2006.  
  2007.     if (LoadPathData(mapname.string) == 2)
  2008.     {
  2009.         //enter "edit" mode if cheats enabled?
  2010.     }
  2011.  
  2012.     trap_Cvar_Update(&bot_wp_edit);
  2013.  
  2014.     if (bot_wp_edit.value)
  2015.     {
  2016.         gBotEdit = 1;
  2017.     }
  2018.     else
  2019.     {
  2020.         gBotEdit = 0;
  2021.     }
  2022.  
  2023.     //set the flag entities
  2024.     while (i < MAX_GENTITIES)
  2025.     {
  2026.         ent = &g_entities[i];
  2027.  
  2028.         if (ent && ent->inuse && ent->classname)
  2029.         {
  2030.             if (!eFlagRed && strcmp(ent->classname, "team_CTF_redflag") == 0)
  2031.             {
  2032.                 eFlagRed = ent;
  2033.             }
  2034.             else if (!eFlagBlue && strcmp(ent->classname, "team_CTF_blueflag") == 0)
  2035.             {
  2036.                 eFlagBlue = ent;
  2037.             }
  2038.  
  2039.             if (eFlagRed && eFlagBlue)
  2040.             {
  2041.                 break;
  2042.             }
  2043.         }
  2044.  
  2045.         i++;
  2046.     }
  2047. }
  2048.  
  2049. int AcceptBotCommand(char *cmd, gentity_t *pl)
  2050. {
  2051.     int OptionalArgument, i;
  2052.     int FlagsFromArgument;
  2053.     char *OptionalSArgument, *RequiredSArgument;
  2054.     vmCvar_t mapname;
  2055.  
  2056.     if (!gBotEdit)
  2057.     {
  2058.         return 0;
  2059.     }
  2060.  
  2061.     OptionalArgument = 0;
  2062.     i = 0;
  2063.     FlagsFromArgument = 0;
  2064.     OptionalSArgument = NULL;
  2065.     RequiredSArgument = NULL;
  2066.  
  2067.     //if a waypoint editing related command is issued, bots will deactivate.
  2068.     //once bot_wp_save is issued and the trail is recalculated, bots will activate again.
  2069.  
  2070.     if (!pl || !pl->client)
  2071.     {
  2072.         return 0;
  2073.     }
  2074.  
  2075.     if (Q_stricmp (cmd, "bot_wp_cmdlist") == 0) //lists all the bot waypoint commands.
  2076.     {
  2077.         Com_Printf(S_COLOR_YELLOW "bot_wp_add" S_COLOR_WHITE " - Add a waypoint (optional int parameter will insert the point after the specified waypoint index in a trail)\n\n");
  2078.         Com_Printf(S_COLOR_YELLOW "bot_wp_rem" S_COLOR_WHITE " - Remove a waypoint (removes last unless waypoint index is specified as a parameter)\n\n");
  2079.         Com_Printf(S_COLOR_YELLOW "bot_wp_addflagged" S_COLOR_WHITE " - Same as wp_add, but adds a flagged point (type bot_wp_addflagged for help)\n\n");
  2080.         Com_Printf(S_COLOR_YELLOW "bot_wp_switchflags" S_COLOR_WHITE " - Switches flags on an existing waypoint (type bot_wp_switchflags for help)\n\n");
  2081.         Com_Printf(S_COLOR_YELLOW "bot_wp_tele" S_COLOR_WHITE " - Teleport yourself to the specified waypoint's location\n");
  2082.         Com_Printf(S_COLOR_YELLOW "bot_wp_killoneways" S_COLOR_WHITE " - Removes oneway (backward and forward) flags on all waypoints in the level\n\n");
  2083.         Com_Printf(S_COLOR_YELLOW "bot_wp_save" S_COLOR_WHITE " - Saves all waypoint data into a file for later use\n");
  2084.  
  2085.         return 1;
  2086.     }
  2087.  
  2088.     if (Q_stricmp (cmd, "bot_wp_add") == 0)
  2089.     {
  2090.         gDeactivated = 1;
  2091.         OptionalSArgument = ConcatArgs( 1 );
  2092.  
  2093.         if (OptionalSArgument)
  2094.         {
  2095.             OptionalArgument = atoi(OptionalSArgument);
  2096.         }
  2097.  
  2098.         if (OptionalSArgument && OptionalSArgument[0])
  2099.         {
  2100.             CreateNewWP_InTrail(pl->client->ps.origin, 0, OptionalArgument);
  2101.         }
  2102.         else
  2103.         {
  2104.             CreateNewWP(pl->client->ps.origin, 0);
  2105.         }
  2106.         return 1;
  2107.     }
  2108.  
  2109.     if (Q_stricmp (cmd, "bot_wp_rem") == 0)
  2110.     {
  2111.         gDeactivated = 1;
  2112.  
  2113.         OptionalSArgument = ConcatArgs( 1 );
  2114.  
  2115.         if (OptionalSArgument)
  2116.         {
  2117.             OptionalArgument = atoi(OptionalSArgument);
  2118.         }
  2119.  
  2120.         if (OptionalSArgument && OptionalSArgument[0])
  2121.         {
  2122.             RemoveWP_InTrail(OptionalArgument);
  2123.         }
  2124.         else
  2125.         {
  2126.             RemoveWP();
  2127.         }
  2128.  
  2129.         return 1;
  2130.     }
  2131.  
  2132.     if (Q_stricmp (cmd, "bot_wp_tele") == 0)
  2133.     {
  2134.         gDeactivated = 1;
  2135.         OptionalSArgument = ConcatArgs( 1 );
  2136.  
  2137.         if (OptionalSArgument)
  2138.         {
  2139.             OptionalArgument = atoi(OptionalSArgument);
  2140.         }
  2141.  
  2142.         if (OptionalSArgument && OptionalSArgument[0])
  2143.         {
  2144.             TeleportToWP(pl, OptionalArgument);
  2145.         }
  2146.         else
  2147.         {
  2148.             Com_Printf(S_COLOR_YELLOW "You didn't specify an index. Assuming last.\n");
  2149.             TeleportToWP(pl, gWPNum-1);
  2150.         }
  2151.         return 1;
  2152.     }
  2153.  
  2154.     if (Q_stricmp (cmd, "bot_wp_addflagged") == 0)
  2155.     {
  2156.         gDeactivated = 1;
  2157.  
  2158.         RequiredSArgument = ConcatArgs( 1 );
  2159.  
  2160.         if (!RequiredSArgument || !RequiredSArgument[0])
  2161.         {
  2162.             Com_Printf(S_COLOR_YELLOW "Flag string needed for bot_wp_addflagged\nj - Jump point\nd - Duck point\nc - Snipe or camp standing\nf - Wait for func\nm - Do not move to when func is under\ns - Snipe or camp\nx - Oneway, forward\ny - Oneway, back\ng - Mission goal\nn - No visibility\nExample (for a point the bot would jump at, and reverse on when traveling a trail backwards):\nbot_wp_addflagged jx\n");
  2163.             return 1;
  2164.         }
  2165.  
  2166.         while (RequiredSArgument[i])
  2167.         {
  2168.             if (RequiredSArgument[i] == 'j')
  2169.             {
  2170.                 FlagsFromArgument |= WPFLAG_JUMP;
  2171.             }
  2172.             else if (RequiredSArgument[i] == 'd')
  2173.             {
  2174.                 FlagsFromArgument |= WPFLAG_DUCK;
  2175.             }
  2176.             else if (RequiredSArgument[i] == 'c')
  2177.             {
  2178.                 FlagsFromArgument |= WPFLAG_SNIPEORCAMPSTAND;
  2179.             }
  2180.             else if (RequiredSArgument[i] == 'f')
  2181.             {
  2182.                 FlagsFromArgument |= WPFLAG_WAITFORFUNC;
  2183.             }
  2184.             else if (RequiredSArgument[i] == 's')
  2185.             {
  2186.                 FlagsFromArgument |= WPFLAG_SNIPEORCAMP;
  2187.             }
  2188.             else if (RequiredSArgument[i] == 'x')
  2189.             {
  2190.                 FlagsFromArgument |= WPFLAG_ONEWAY_FWD;
  2191.             }
  2192.             else if (RequiredSArgument[i] == 'y')
  2193.             {
  2194.                 FlagsFromArgument |= WPFLAG_ONEWAY_BACK;
  2195.             }
  2196.             else if (RequiredSArgument[i] == 'g')
  2197.             {
  2198.                 FlagsFromArgument |= WPFLAG_GOALPOINT;
  2199.             }
  2200.             else if (RequiredSArgument[i] == 'n')
  2201.             {
  2202.                 FlagsFromArgument |= WPFLAG_NOVIS;
  2203.             }
  2204.             else if (RequiredSArgument[i] == 'm')
  2205.             {
  2206.                 FlagsFromArgument |= WPFLAG_NOMOVEFUNC;
  2207.             }
  2208.  
  2209.             i++;
  2210.         }
  2211.  
  2212.         OptionalSArgument = ConcatArgs( 2 );
  2213.  
  2214.         if (OptionalSArgument)
  2215.         {
  2216.             OptionalArgument = atoi(OptionalSArgument);
  2217.         }
  2218.  
  2219.         if (OptionalSArgument && OptionalSArgument[0])
  2220.         {
  2221.             CreateNewWP_InTrail(pl->client->ps.origin, FlagsFromArgument, OptionalArgument);
  2222.         }
  2223.         else
  2224.         {
  2225.             CreateNewWP(pl->client->ps.origin, FlagsFromArgument);
  2226.         }
  2227.         return 1;
  2228.     }
  2229.  
  2230.     if (Q_stricmp (cmd, "bot_wp_switchflags") == 0)
  2231.     {
  2232.         gDeactivated = 1;
  2233.  
  2234.         RequiredSArgument = ConcatArgs( 1 );
  2235.  
  2236.         if (!RequiredSArgument || !RequiredSArgument[0])
  2237.         {
  2238.             Com_Printf(S_COLOR_YELLOW "Flag string needed for bot_wp_switchflags\nType bot_wp_addflagged for a list of flags and their corresponding characters, or use 0 for no flags.\nSyntax: bot_wp_switchflags <flags> <n>\n");
  2239.             return 1;
  2240.         }
  2241.  
  2242.         while (RequiredSArgument[i])
  2243.         {
  2244.             if (RequiredSArgument[i] == 'j')
  2245.             {
  2246.                 FlagsFromArgument |= WPFLAG_JUMP;
  2247.             }
  2248.             else if (RequiredSArgument[i] == 'd')
  2249.             {
  2250.                 FlagsFromArgument |= WPFLAG_DUCK;
  2251.             }
  2252.             else if (RequiredSArgument[i] == 'c')
  2253.             {
  2254.                 FlagsFromArgument |= WPFLAG_SNIPEORCAMPSTAND;
  2255.             }
  2256.             else if (RequiredSArgument[i] == 'f')
  2257.             {
  2258.                 FlagsFromArgument |= WPFLAG_WAITFORFUNC;
  2259.             }
  2260.             else if (RequiredSArgument[i] == 's')
  2261.             {
  2262.                 FlagsFromArgument |= WPFLAG_SNIPEORCAMP;
  2263.             }
  2264.             else if (RequiredSArgument[i] == 'x')
  2265.             {
  2266.                 FlagsFromArgument |= WPFLAG_ONEWAY_FWD;
  2267.             }
  2268.             else if (RequiredSArgument[i] == 'y')
  2269.             {
  2270.                 FlagsFromArgument |= WPFLAG_ONEWAY_BACK;
  2271.             }
  2272.             else if (RequiredSArgument[i] == 'g')
  2273.             {
  2274.                 FlagsFromArgument |= WPFLAG_GOALPOINT;
  2275.             }
  2276.             else if (RequiredSArgument[i] == 'n')
  2277.             {
  2278.                 FlagsFromArgument |= WPFLAG_NOVIS;
  2279.             }
  2280.             else if (RequiredSArgument[i] == 'm')
  2281.             {
  2282.                 FlagsFromArgument |= WPFLAG_NOMOVEFUNC;
  2283.             }
  2284.  
  2285.             i++;
  2286.         }
  2287.  
  2288.         OptionalSArgument = ConcatArgs( 2 );
  2289.  
  2290.         if (OptionalSArgument)
  2291.         {
  2292.             OptionalArgument = atoi(OptionalSArgument);
  2293.         }
  2294.  
  2295.         if (OptionalSArgument && OptionalSArgument[0])
  2296.         {
  2297.             WPFlagsModify(OptionalArgument, FlagsFromArgument);
  2298.         }
  2299.         else
  2300.         {
  2301.             Com_Printf(S_COLOR_YELLOW "Waypoint number (to modify) needed for bot_wp_switchflags\nSyntax: bot_wp_switchflags <flags> <n>\n");
  2302.         }
  2303.         return 1;
  2304.     }
  2305.  
  2306.     if (Q_stricmp (cmd, "bot_wp_killoneways") == 0)
  2307.     {
  2308.         i = 0;
  2309.  
  2310.         while (i < gWPNum)
  2311.         {
  2312.             if (gWPArray[i] && gWPArray[i]->inuse)
  2313.             {
  2314.                 if (gWPArray[i]->flags & WPFLAG_ONEWAY_FWD)
  2315.                 {
  2316.                     gWPArray[i]->flags -= WPFLAG_ONEWAY_FWD;
  2317.                 }
  2318.                 if (gWPArray[i]->flags & WPFLAG_ONEWAY_BACK)
  2319.                 {
  2320.                     gWPArray[i]->flags -= WPFLAG_ONEWAY_BACK;
  2321.                 }
  2322.             }
  2323.  
  2324.             i++;
  2325.         }
  2326.  
  2327.         return 1;
  2328.     }
  2329.  
  2330.     if (Q_stricmp (cmd, "bot_wp_save") == 0)
  2331.     {
  2332.         gDeactivated = 0;
  2333.         trap_Cvar_Register( &mapname, "mapname", "", CVAR_SERVERINFO | CVAR_ROM, 0.0, 0.0 );
  2334.         SavePathData(mapname.string);
  2335.         return 1;
  2336.     }
  2337.  
  2338.     return 0;
  2339. }
  2340.